import 'server-only';

import { BizError, ErrorCode } from '@/service/error';
import type { Pagination } from '@/service/type/pagination';
import { Jieba, TfIdf } from '@node-rs/jieba';
import { dict, idf } from '@node-rs/jieba/dict';
import { PAGE_NUM, PAGE_SIZE } from './constants';
import env from './env';

const jieba = Jieba.withDict(dict);
const tfIdf = TfIdf.withDict(idf);

export const extractWords = (text: string, topN = 10) =>
  tfIdf.extractKeywords(jieba, text, topN).map((i) => i.keyword);

const gorseFetch = (api: string, options?: RequestInit) => {
  return fetch(`${env.GORSE_URL}/api${api}`, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': env.GORSE_KEY,
      ...options?.headers,
    },
  });
};

const insertUsers = async (ids: string[]) => {
  await gorseFetch('/users', {
    method: 'POST',
    body: JSON.stringify(
      ids.map((id) => ({
        UserId: id,
      })),
    ),
  });
};

const insertItems = async (
  items: {
    id: string;
    labels: string[];
  }[],
) => {
  await gorseFetch('/items', {
    method: 'POST',
    body: JSON.stringify(
      items.map((i) => ({
        ItemId: i.id,
        Labels: i.labels,
        Timestamp: new Date().toISOString(),
      })),
    ),
  });
};

const insertFeedbacks = async (
  feedbacks: { itemId: string; userId: string; type: 'order' | 'click' }[],
) => {
  await gorseFetch('/feedback', {
    method: 'POST',
    body: JSON.stringify(
      feedbacks.map((i) => ({
        FeedbackType: i.type,
        ItemId: i.itemId,
        UserId: i.userId,
        Timestamp: new Date().toISOString(),
      })),
    ),
  });
};

const getUserRecommendations = async ({
  userId,
  page = PAGE_NUM,
  pageSize = PAGE_SIZE,
}: { userId: string; page?: number; pageSize?: number }) => {
  const searchParams = new URLSearchParams({
    n: (pageSize + 1).toString(),
    offset: ((page - 1) * pageSize).toString(),
  });
  const res = await gorseFetch(
    `/recommend/${userId}?${searchParams.toString()}`,
    {
      method: 'GET',
    },
  );
  const data = await res.json();
  if (data && data.length > pageSize) {
    data.pop();
    return {
      data,
      next: page + 1,
    } as Pagination<string[]>;
  }
  return {
    data,
    next: null,
  } as Pagination<string[]>;
};

const updateItem = async ({ id, labels }: { id: string; labels: string[] }) => {
  await gorseFetch(`/item/${id}`, {
    method: 'PATCH',
    body: JSON.stringify({
      Labels: labels,
      Timestamp: new Date().toISOString(),
    }),
  });
};

const mergeItemLabels = async ({
  id,
  labels,
}: { id: string; labels: string[] }) => {
  const item = await getItem(id);
  if (!item) return;
  if (labels.length < 30)
    labels.push(...item.Labels.slice(0, 30 - labels.length));
  await updateItem({ id, labels: [...new Set(labels)] });
};

const getItem = async (
  id: string,
): Promise<{
  ItemId: string;
  Labels: string[];
  IsHidden: boolean;
  Timestamp: string;
}> => {
  const res = await gorseFetch(`/item/${id}`, {
    method: 'GET',
  });
  return await res.json();
};

const rmItem = async (id: string) => {
  await gorseFetch(`/item/${id}`, {
    method: 'DELETE',
  });
};

export const gorse = {
  insertUsers,
  insertItems,
  insertFeedbacks,
  getUserRecommendations,
  updateItem,
  getItem,
  mergeItemLabels,
  rmItem,
};
